/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.analyzer;

import java.util.Comparator;
import java.util.List;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGConstructorList;
import org.jfab.core.collection.CGFieldList;
import org.jfab.core.collection.CGInitializationBlockList;
import org.jfab.core.collection.CGMethodList;
import org.jfab.core.collection.CGTypeList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.ImportMap;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.factory.CGAnnotationFactory;
import org.jfab.core.factory.CGClassFactory;
import org.jfab.core.factory.CGTypeFactory;
import org.jfab.core.factory.StringFactory;
import org.jfab.core.model.CGClass;
import org.jfab.core.model.CGType;
import org.jfab.core.model.CGTypeUtilities;
import org.jfab.core.model.TypeFactory;

/**
 * Provides a class analyzer.
 */
public final class ClassAnalyzer extends
        AbstractCompilationUnitAnalyzer<CGClass, Class<?>>
{
    /** Class factory. */
    private final CGClassFactory _classFactory;

    /** Constructor analyzer. */
    private final ConstructorAnalyzer _constructorAnalyzer;

    /** Field analyzer. */
    private final FieldAnalyzer _fieldAnalyzer;

    /** Method analyzer. */
    private final MethodAnalyzer _methodAnalyzer;

    /**
     * Construct this object with the given parameters.
     * 
     * @param typeFactory Type factory.
     * @param cgTypeUtils Code generator type utilities. (required)
     * @param cgTypeFactory Code generator type factory.
     * @param stringFactory String factory.
     * @param annotationFactory Annotation factory.
     * @param comparator Comparator.
     * @param classFactory Class factory.
     * @param constructorAnalyzer Constructor analyzer.
     * @param fieldAnalyzer Field analyzer.
     * @param methodAnalyzer Method analyzer.
     */
    public ClassAnalyzer(final TypeFactory typeFactory,
            final CGTypeUtilities cgTypeUtils,
            final CGTypeFactory cgTypeFactory,
            final StringFactory stringFactory,
            final CGAnnotationFactory annotationFactory,
            final Comparator<CGClass> comparator,
            final CGClassFactory classFactory,
            final FieldAnalyzer fieldAnalyzer,
            final ConstructorAnalyzer constructorAnalyzer,
            final MethodAnalyzer methodAnalyzer)
    {
        super(typeFactory, cgTypeUtils, cgTypeFactory, stringFactory,
                annotationFactory, comparator);

        _classFactory = classFactory;
        _fieldAnalyzer = fieldAnalyzer;
        _constructorAnalyzer = constructorAnalyzer;
        _methodAnalyzer = methodAnalyzer;
    }

    @Override
    public final CGClass analyze(final Class<?> aClass, final String javaSource)
    {
        Validate.notNull(aClass, "aClass is null");

        CGClass answer = null;

        final CGTypeList referencedTypes = null;
        final String packageName = aClass.getPackage().getName();
        final CommentList commentLines = parseCommentLines(javaSource);
        final CGAnnotationList annotations = null;
        final ModifierList modifiers = new ModifierList(aClass.getModifiers());
        final String shortClassName = aClass.getSimpleName();
        final List<String> typeParameters = null;
        final CGType extendsType = parseExtendsType(packageName,
                aClass.getSuperclass(), javaSource);
        final CGTypeList implementsInterfaces = parseImplementsTypes(
                packageName, aClass.getInterfaces(), javaSource);
        final CGInitializationBlockList initializationBlocks = null;
        final CGConstructorList constructors = new CGConstructorList(
                getConstructorAnalyzer().analyze(
                        aClass.getDeclaredConstructors(), javaSource));
        final CGFieldList fields = new CGFieldList(getFieldAnalyzer().analyze(
                aClass.getDeclaredFields(), javaSource));
        final CGMethodList methods = new CGMethodList(getMethodAnalyzer()
                .analyze(aClass.getDeclaredMethods(), javaSource));

        answer = getClassFactory().createClass(referencedTypes, packageName,
                commentLines, annotations, modifiers, shortClassName,
                typeParameters, extendsType, implementsInterfaces,
                initializationBlocks, constructors, fields, methods);
        answer.setJavaType(getCgTypeFactory().createType(aClass));

        return answer;
    }

    /**
     * @return the classFactory
     */
    private CGClassFactory getClassFactory()
    {
        return _classFactory;
    }

    /**
     * @return the constructorAnalyzer
     */
    private ConstructorAnalyzer getConstructorAnalyzer()
    {
        return _constructorAnalyzer;
    }

    /**
     * @return the fieldAnalyzer
     */
    private FieldAnalyzer getFieldAnalyzer()
    {
        return _fieldAnalyzer;
    }

    /**
     * @return the methodAnalyzer
     */
    private MethodAnalyzer getMethodAnalyzer()
    {
        return _methodAnalyzer;
    }

    /**
     * @param packageName Package name.
     * @param extendsClass Extends class.
     * @param sourceCode Source code.
     * 
     * @return implements types parsed from the given parameters.
     */
    private CGType parseExtendsType(final String packageName,
            final Class<?> extendsClass, final String sourceCode)
    {
        CGType answer = null;

        String text = StringUtils.substringBetween(sourceCode, "extends",
                "implements");

        if (StringUtils.isEmpty(text))
        {
            text = StringUtils.substringBetween(sourceCode, "extends", "{");
        }

        if (StringUtils.isNotEmpty(text))
        {
            text = text.replaceAll("\n", "");
            text = text.trim();

            final CGTypeFactory cgTypeFactory = getCgTypeFactory();
            final ImportMap importMap = analyzeImports(sourceCode);
            answer = cgTypeFactory.createType(packageName, importMap, text);
        }

        return answer;
    }

    /**
     * @param packageName Package name.
     * @param implementsClasses Implementation classes.
     * @param sourceCode Source code.
     * 
     * @return implements types parsed from the given parameters.
     */
    private CGTypeList parseImplementsTypes(final String packageName,
            final Class<?>[] implementsClasses, final String sourceCode)
    {
        CGTypeList answer = null;

        if ((implementsClasses != null) && (implementsClasses.length > 0))
        {
            answer = new CGTypeList();

            final ImportMap importMap = analyzeImports(sourceCode);
            final int size = implementsClasses.length;
            final String text = StringUtils.substringBetween(sourceCode,
                    "implements", "{");

            final String[] texts = text.split(",");
            final CGTypeFactory cgTypeFactory = getCgTypeFactory();

            for (int i = 0; i < size; i++)
            {
                final CGType cgType = cgTypeFactory.createType(packageName,
                        importMap, texts[i]);
                answer.add(cgType);
            }
        }

        return answer;
    }
}
